<?php

/**
 * 模板类
 */
class Template {

    protected static $_instance = null;
    public $options = array('tpldir' => '', 'cachedir' => '', 'isfilter' => 1, 'suffix' => 'html');
    private $data = array();

    function __construct() {
        if (Yaf_Registry::get("config")->template) {
            $this->options = array_merge($this->options, Yaf_Registry::get("config")->template->toArray());
        }
    }

    /**
     * 单例方法
     * @return Template
     */
    public static function getInstance() {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    /**
     * 设置模板中显示的变量
     * @param string $key 变量名称
     * @param mixed $value 变量值
     */
    function assign($key, $value = null) {
        if (isset($value)) {
            $this->data[$key] = $value;
        } else {
            foreach ($key as $k => $v) {
                if (is_int($k)) {
                    $this->data[] = $v;
                } else {
                    $this->data[$key] = $v;
                }
            }
        }
    }

    /**
     * 返回一个模板解析后的结果,不执行输出
     * @param string $name 模板文件名称
     * @param mixed $data 向模板传输的数据，默认为null
     * @return string 解析后的数据
     */
    function fetch($name, $data = null) {
        ob_start();
        $this->display($name, $data);
        $content = ob_get_contents();
        ob_end_clean();
        return $content;
    }

    /**
     * 显示一个模板解析后的结果，执行输出
     * @param string $name 模板文件名称
     * @param array $data 向模板传输的数据
     */
    function display($name, $data = array()) {
        extract($this->data);
        if (!empty($data))
            extract($data);

        $view = $this->options['tpldir'] . '/' . $name . '.' . $this->options['suffix'];
        $cache = $this->options['cachedir'] . '/' . $name . '.php';
        if (!file_exists($view))
            $this->error('Template not found!');
        if (file_exists($cache) && filemtime($view) < @filemtime($cache)) {
            include($cache);
        } else {
            $this->parse($view, $cache);
            include($cache);
        }
    }

    /**
     * 显示一个模板解析后的结果，执行输出
     * @param string $name 模板文件名称
     * @param mixed $data 向模板传输的数据，默认为null
     */
    function view($name, $data = null) {
        $this->display($name, $data = null);
    }

    /**
     * 显示一个系统模板解析后的结果，执行输出
     * @param string $name 模板文件名称
     * @param mixed $data 向模板传输的数据，默认为null
     */
    function error($msg) {
        throw new Exception($msg);
    }

    /**
     * 解析模板
     * @param string $view 模板文件路径
     * @param string $cache 缓存的子模板数据，默认为null
     */
    function parse($view, $cache = null) {
        if (!file_exists($view))
            $this->error('Template not found!');
        $view = file_get_contents($view);

        /* 将<!--{ }-->替换为{ } */
        $view = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $view);
        /* 清除注释 */
        $view = preg_replace("/\{\*(.*?)\*\}/", '', $view);
        /* 解析{ $var }基本变量输出标签 */
        $view = preg_replace('/\{\$(.+?)\}/', '<?php echo $\\1; ?>', $view);
        /* 解析{ include file=}标签 */
        $view = preg_replace('/\{\s*include\s+file=[\'"]?(.+?)[\'"]?\s*\}/', '{view \\1}', $view);
        /* 解析{ include  }标签 */
        $view = preg_replace('/\{\s*include\s+(.+?)\s*\}/', '<?php include APPPATH.\'views/\\1\'; ?>', $view);
        /* 解析{ html  }标签 */
        $view = preg_replace('/\{\s*html\s+(.+?)\s*\}/', '<?php echo file_get_contents(\\1); ?>', $view);
        /* 解析{ php  }标签 */
        $view = preg_replace('/\{\s*php\s+(.+?)\s*\}/', '<?php \\1 ?>', $view);
        /* 解析{ if  }标签 */
        $view = preg_replace('/\{\s*if\s+(.+?)\s*\}/', '<?php if (\\1) { ?>', $view);
        /* 解析{ elseif  }标签 */
        $view = preg_replace('/\{\s*elseif\s+(.+?)\s*\}/', '<?php } elseif(\\1) { ?>', $view);
        /* 解析{ while  }标签 */
        $view = preg_replace('/\{\s*while\s+(.+?)\s*\}/', '<?php while (\\1) { ?>', $view);
        /* 解析{ foreach  }标签 */
        $view = preg_replace('/\{\s*foreach\s+(.+?)\s*\}/', '<?php foreach (\\1) { ?>', $view);
        /* 解析{ for  }标签 */
        $view = preg_replace('/\{\s*for\s+(.+?)\s*\}/', '<?php for (\\1) { ?>', $view);
        /* 解析{ else }标签 */
        $view = preg_replace('/\{\s*else\s*\}/', '<?php } else { ?>', $view);
        /* 解析{ /while },{ /if },{ /for },{ /foreach }标签 */
        $view = preg_replace('#\{\s*/[a-z]+\s*\}#', '<?php } ?>', $view);
        /* 递归解析嵌套的子模板 */
        if (preg_match_all('/\{\s*view\s+(.+?)\s*\}/', $view, $viewsInView)) {
            /* 循环解析此模板中所有子模板，并将解析结果保存到数组$views */
            $viewInViews = array();
            foreach ($viewsInView[1] as $viewInView) {
                $viewInView = $this->dir . '/' . $viewInView;
                //$viewInViews[] = $this->parse(eval('return '.$viewInView.';'));
                $viewInViews[] = $this->parse($viewInView);
            }
            /* 将该模板中所有子模板的标签分别替换为其对应的解析结果 */
            $view = str_replace($viewsInView[0], $viewInViews, $view);
        }
        /* 如果开启了缓存过滤，则过滤掉其中多余空格、Tab、回车 */
        if ($this->options['isfilter'])
            $view = preg_replace("/ \?\>[\n\r]*\<\? /s", " ", $view);
        /* 合并相邻标签的解析结果 */
        $view = preg_replace('/\?>\s*<\?php/', '', $view);
        $view = preg_replace('/\?>\s*<\?/', '', $view);

        /* 如果是在解析子模板，则返回解析结果 */
        if ($cache == null)
            return $view;
        Tools::mkdirs(dirname($cache));
        $view = '<? defined("APPLICATION_PATH") or die("Access Denied!"); ?>' . $view;
        @file_put_contents($cache, $view);
    }

}
